#include "common.h"

#include <avr/interrupt.h>
#include <avr/io.h>

void operator delete(void *, unsigned int) {}

extern "C" void __cxa_pure_virtual() {}

std::uint64_t gpt_system_tick = 0;

void adjust_timer(const rotor_light::Duration &value) {
  gpt_system_tick += value;
}

rl::TimePoint get_now() {
  // The entire system-tick is composed of the static
  // 64-bit variable mcal_gpt_system_tick and the 8-bit
  // timer register TCNT0. These are concatenated together
  // in software as a cohesive, consistent 64-bit tick.

  // This subroutine returns the concatenated 64-bit
  // mcal_gpt_system_tick/TCNT0 64-bit system tick.
  // A multiple read of the tick is used in order
  // to ensure data consistency.

  // Do the first read of the TIMER0 counter and the system tick.
  const auto tim0_cnt_1 = TCNT0;
  const auto sys_tick_1 = gpt_system_tick;

  // Do the second read of the TIMER0 counter.
  const auto tim0_cnt_2 = TCNT0;

  // Perform the consistency check to obtain the concatenated,
  // consistent, 64-bit system-tick.
  const auto consistent_microsecond_tick =
      ((tim0_cnt_2 >= tim0_cnt_1)
           ? static_cast<std::uint64_t>(
                 sys_tick_1 | static_cast<std::uint8_t>(tim0_cnt_1 >> 1U))
           : static_cast<std::uint64_t>(
                 gpt_system_tick |
                 static_cast<std::uint8_t>(tim0_cnt_2 >> 1U)));

  return consistent_microsecond_tick;
}

void enable_timer() {
  // We will now initialize the TIMER0 clock and interrupt.
  // Clear the TIMER0 overflow flag.
  TIFR0 = static_cast<std::uint8_t>(1U << TOV0);

  // Enable the TIMER0 overflow interrupt.
  TIMSK0 = static_cast<std::uint8_t>(1U << TOIE0);

  // Set the TIMER0 clock source to f_osc/8 = 2MHz and begin counting.
  TCCR0B = static_cast<std::uint8_t>(1U << CS01);
}

void disable_timer() {
  // erase TIMER0 clock source;
  TCCR0B = 0;

  // Clear the TIMER0 overflow flag.
  TIMSK0 = 0;

  // Enable the TIMER0 overflow interrupt.
  TIMSK0 = 0;
}

ISR(TIMER0_OVF_vect) {
  // Increment the 64-bit system tick with 0x80, representing 128 microseconds.
  gpt_system_tick += UINT8_C(0x80);
}
